package com.umeox.moto.common.recorder;

import android.content.Context;
import android.media.AudioManager;
import android.media.MediaMetadataRetriever;
import android.media.MediaRecorder;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;

import com.umeox.moto.common.BuildConfig;
import com.umeox.moto.common.log.LogUtils;
import com.umeox.moto.common.utils.AndroidUtils;
import com.umeox.moto.common.utils.FileUtils;

import java.io.File;
import java.io.RandomAccessFile;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class SilentRecord {
    private final Object mLock = new Object();

    private int maxDurationMs = 15 * 1000;//录音文件最大长度
    private int minDurationMs = 700;//录音文件最小长度
    private long millisUntilFinished = 0;//记录录音时剩余时长

    private MediaRecorder mRecorder;
    private ScheduledThreadPoolExecutor countdownExecutor;
    private Future countDownTask;
    private RecorderState mState = RecorderState.STATE_IDLE;
    private Handler mHandler;

    private String dir;
    private String recordPath;
    private RecordListener mListener;
    private AmrNameGenerator mNameGenerator;
    // 间隔取样时间
    private int mSampleGapTime;
    private AudioManager mAudioManager;

    public SilentRecord(Context context, String dir, int maxDuration, RecordListener listener) {
        this(dir, maxDuration, 0, listener, null, 200);
        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);

    }

    public SilentRecord(String dir, int maxDuration, int minDuration, RecordListener listener) {
        this(dir, maxDuration, minDuration, listener, null, 200);
    }

    public SilentRecord(String dir, int maxDuration, int minDuration, RecordListener listener, AmrNameGenerator generator, int sampleGap) {
        this.mListener = listener;
        if (maxDuration > 0) {
            maxDurationMs = maxDuration;
        }
        if (minDuration > 0
                && minDuration < maxDuration) {
            minDurationMs = minDuration;
        }

        countdownExecutor = new ScheduledThreadPoolExecutor(1,
                new ThreadFactory() {
                    private AtomicInteger count = new AtomicInteger(0);

                    @Override
                    public Thread newThread(Runnable r) {
                        Thread thread = new Thread(r);
                        thread.setName("thread-recorder-" + count.addAndGet(1));
                        return thread;
                    }
                });
        //延时的schedule在shutdown后，不会继续执行没到时间的任务
        // 默认是true，会执行一次正在倒计时的任务再结束
        countdownExecutor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
        //延时的scheduleAtFixedRate/scheduleWithFixedDelay在shutdown后，不会继续执行没到时间的任务
        // 默认是true，会执行一次正在倒计时的任务再结束
        countdownExecutor.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);

        if (generator == null) {
            this.mNameGenerator = new DefaultAmrNameGenerator();
        } else {
            this.mNameGenerator = generator;
        }
        this.mHandler = new EventHandler(Looper.getMainLooper());
        this.mSampleGapTime = sampleGap;
        this.dir = dir;
        FileUtils.createFolder(dir);
        initMediaRecorder();
    }

    /**
     * 得到amr的时长
     *
     * @param file
     * @return
     */
    @Deprecated
    public static long getAmrDuration(File file) {
        long duration = -1;
        int[] packedSize = {12, 13, 15, 17, 19, 20, 26, 31, 5, 0, 0, 0, 0, 0, 0, 0};
        RandomAccessFile randomAccessFile = null;
        try {
            randomAccessFile = new RandomAccessFile(file, "rw");
            long length = file.length();//文件的长度
            int pos = 6;//设置初始位置
            int frameCount = 0;//初始帧数
            int packedPos = -1;
            /////////////////////////////////////////////////////
            byte[] datas = new byte[1];//初始数据值
            while (pos <= length) {
                randomAccessFile.seek(pos);
                if (randomAccessFile.read(datas, 0, 1) != 1) {
                    duration = length > 0 ? ((length - 6) / 650) : 0;
                    break;
                }
                packedPos = (datas[0] >> 3) & 0x0F;
                pos += packedSize[packedPos] + 1;
                frameCount++;
            }
            /////////////////////////////////////////////////////
            duration += frameCount * 20;//帧数*20
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        } finally {
            try {
                if (randomAccessFile != null) {
                    randomAccessFile.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
                return 0;
            }
        }
        return duration;
    }

    public static int getFileDuration(String filePath) {
        MediaMetadataRetriever mmr = new MediaMetadataRetriever();
        try {
            mmr.setDataSource(filePath);
            //播放时长单位为毫秒
            String duration = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
            // 从api level 14才有，即从ICS4.0才有此功能
            String bitrate = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE);
            if (BuildConfig.DEBUG) {
                LogUtils.i(String.format("文件路劲:%1s ,比特率:%2s ,长度:%3s", filePath, bitrate, duration));
            }
            return Integer.parseInt(duration);
        } catch (Exception e) {
            return 0;
        }
    }

    public String getRecordPath() {
        if (mState != RecorderState.STATE_ERROR) {
            return recordPath;
        } else {
            return "";
        }
    }

    public RecorderState getState() {
        return mState;
    }

    public void release() {
        releaseMediaRecorder();
        this.mNameGenerator = null;
        this.mListener = null;
    }

    private void initMediaRecorder() {
        if (mRecorder != null) {
            release();
        }
        mRecorder = new MediaRecorder();
        configureMediaRecorder();
    }

    private void configureMediaRecorder() {
        //设置音源为Micphone
        //MediaRecorder对象由Initial状态进入Initialized状态
        mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        //设置封装格式
        //MediaRecorder对象由Initialized状态进入DataSourceConfigured状态
        mRecorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);
        recordPath = dir + "/" + AndroidUtils.getDeviceId() + "_" + generateAmrFileName();
        mRecorder.setOutputFile(recordPath);
        //设置编码格式
        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
        //设置最大录音时长
        mRecorder.setMaxDuration(maxDurationMs);
        mRecorder.setOnInfoListener((mr, what, extra) -> {
            if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
                LogUtils.e("A maximum duration (" + maxDurationMs + ") had been setup and has now been reached.");
                stopRecord();
            }
        });

        if (BuildConfig.DEBUG) {
            LogUtils.w("recordPath:" + recordPath + ",maxDuration:" + maxDurationMs);
        }
    }

    private void releaseMediaRecorder() {
        if (mRecorder != null) {
            mRecorder.setOnInfoListener(null);
            mRecorder.release();
            mRecorder = null;
        }
    }

    private void resetMediaRecorder() {
        if (BuildConfig.DEBUG) {
            LogUtils.d("resetMediaRecorder");
        }

        if (!countDownTask.isDone()
                || !countDownTask.isCancelled()) {
            countDownTask.cancel(true);
        }

        if (mRecorder != null) {
            mRecorder.reset();//MediaRecorder对象进入Initial状态。
            configureMediaRecorder();
        }
    }

    private String generateAmrFileName() {
        String name = mNameGenerator.generateName(System.currentTimeMillis());
        if (TextUtils.isEmpty(name)) {
            mNameGenerator = new DefaultAmrNameGenerator();
            return generateAmrFileName();
        }
        if (name.endsWith(".amr")) {
            return name;
        } else {
            return String.format("%1$s.%2$s", name, "amr");
        }
    }

    public void startRecord() {
        mAudioManager.requestAudioFocus(null, AudioManager.STREAM_VOICE_CALL, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);


        synchronized (mLock) {
            if (BuildConfig.DEBUG) {
                LogUtils.d("startRecord");
            }
            if (mState != RecorderState.STATE_IDLE) {
                resetRecorderState();
            }
            try {
                mRecorder.prepare();
                mRecorder.start();
                LogUtils.d("recording");
                mHandler.sendMessage(mHandler.obtainMessage(EventHandler.MSG_RECORD_STATUS_UPDATE, RecorderState.STATE_RECORDING));
                if (countDownTask != null
                        && !countDownTask.isCancelled()) {
                    countDownTask.cancel(true);
                }
                //获取未完成的任务队列
                BlockingQueue<Runnable> queues = countdownExecutor.getQueue();
                if (queues != null && !queues.isEmpty()) {
                    Runnable task = queues.poll();
                    while (task != null) {
                        countdownExecutor.remove(task);
                        task = queues.poll();
                    }
                }

                final int periodMills = 50;
                countDownTask = countdownExecutor.scheduleAtFixedRate(new Runnable() {
                    private AtomicInteger count = new AtomicInteger(-1);

                    @Override
                    public void run() {
                        mHandler.sendMessage(mHandler.obtainMessage(EventHandler.MSG_RECORDING_COUNTDOWN, count.incrementAndGet() * periodMills));
                    }
                }, 0, periodMills, TimeUnit.MILLISECONDS);
            } catch (Exception e) {
                if (BuildConfig.DEBUG) {
                    e.printStackTrace();
                }
                changeState(RecorderState.STATE_ERROR);
            }
        }
    }

    public void stopRecord() {
        mAudioManager.abandonAudioFocus(null);
        synchronized (mLock) {
            if (BuildConfig.DEBUG) {
                LogUtils.d("stopRecord->max:" + maxDurationMs + ",min:" + minDurationMs + ",recordPath：" + recordPath);
            }
            //有最短录音时长限制
            if (minDurationMs < maxDurationMs &&
                    (maxDurationMs - millisUntilFinished) < minDurationMs) {
                mHandler.sendMessage(mHandler.obtainMessage(EventHandler.MSG_RECORD_STATUS_UPDATE, RecorderState.STATE_RECORD_SHORT));
                FileUtils.deleteFile(getRecordPath());
                //如果录音时间太短，start()之后马上调用stop()会抛出异常，所以要延时调用stop
            } else {
                mHandler.sendMessage(mHandler.obtainMessage(EventHandler.MSG_RECORD_STATUS_UPDATE, RecorderState.STATE_RECORD_END));
            }

            if (countDownTask != null) {
                countDownTask.cancel(true);
            }
            if (mRecorder != null) {
                mRecorder.setOnInfoListener(null);
                mRecorder.setOnErrorListener(null);
                try {
                    mRecorder.stop();
                } catch (Exception e) {
                    Log.e("SilentRecord", "stopRecord: ", e);
                    resetMediaRecorder();
                    changeState(RecorderState.STATE_ERROR);
                }
            }

        }
    }

    /**
     * 用户主动取消录音
     */
    public void cancelRecord() {
        synchronized (mLock) {
            if (BuildConfig.DEBUG) {
                LogUtils.d("cancelRecord");
            }
            //用户取消录音，不必判断录音时长
            mHandler.sendMessage(mHandler.obtainMessage(EventHandler.MSG_RECORD_STATUS_UPDATE, RecorderState.STATE_CANCEL));

            if (countDownTask != null) {
                countDownTask.cancel(true);
            }
            try {
                FileUtils.deleteFile(getRecordPath());
            } catch (Exception exception) {
                exception.printStackTrace();
            }
            try {
                if (mRecorder != null) {
                    mRecorder.stop();
                }
            } catch (Exception e) {
                LogUtils.e("cancelRecord error:" + e.getMessage());
                resetMediaRecorder();
            }
        }
    }

    private void resetRecorderState() {
        if (BuildConfig.DEBUG) {
            LogUtils.d("resetRecorderState");
        }
        millisUntilFinished = 0;
        resetMediaRecorder();
        mState = RecorderState.STATE_IDLE;
    }

    private void changeState(RecorderState state) {
        RecorderState oldState = mState;
        mState = state;
        if (BuildConfig.DEBUG) {
            LogUtils.d("change state to " + state);
        }
        if (mState == RecorderState.STATE_RECORDING) {
            mHandler.sendMessage(mHandler.obtainMessage(EventHandler.MSG_MIC_STATUS_UPDATE));
        }
        if (mListener != null) {
            mListener.onRecordStateChanged(oldState, mState);
        }
    }

    /**
     * 更新话筒状态 分贝是也就是相对响度 分贝的计算公式K=20lg(Vo/Vi) Vo当前振幅值 Vi基准值为600：我是怎么制定基准值的呢？
     * 当 [20* Math.log10(mMediaRecorder.getMaxAmplitude() / Vi)==0]的时候vi就是我所需要的基准值
     * 当我不对着麦克风说任何话的时候，测试获得的mMediaRecorder.getMaxAmplitude()值即为基准值。
     * Log.i("mic_", "麦克风的基准值：" + mMediaRecorder.getMaxAmplitude());前提时不对麦克风说任何话
     */
    private void updateMicStatus() {
        if (mRecorder != null && mState == RecorderState.STATE_RECORDING) {
            double ratio = (double) mRecorder.getMaxAmplitude() / 2.5;
            /*if (BuildConfig.DEBUG) {
                LogUtils.e("最大振幅: " + ratio);
                //LogUtils.e("基准值: " + Math.log10(mRecorder.getMaxAmplitude()));
            }*/
            int db = 0;// 分贝
            if (ratio > 1) {
                db = (int) (20 * Math.log10(ratio));
            }
            /*if (BuildConfig.DEBUG) {
                LogUtils.e("分贝值: " + db);
            }*/
            if (mListener != null && db > 0) {
                mListener.onRecordMicStatusUpdate(db);
            }
            mHandler.sendMessageDelayed(mHandler.obtainMessage(EventHandler.MSG_MIC_STATUS_UPDATE), mSampleGapTime);
        }
    }

    public enum RecorderState {
        STATE_IDLE, STATE_RECORDING, STATE_RECORD_END, STATE_RECORD_SHORT, STATE_CANCEL, STATE_ERROR;
    }

    public interface RecordListener {
        void onRecordStateChanged(RecorderState oldState, RecorderState newState);

        void onRecordMicStatusUpdate(int db);

        void onTick(long millisUntilFinished);
    }

    interface AmrNameGenerator {
        String generateName(long time);
    }

    private class DefaultAmrNameGenerator implements AmrNameGenerator {
        // 设置日期格式
        private SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmssSSS", Locale.CHINESE);

        @Override
        public String generateName(long time) {
            return df.format(new Date(time));
        }
    }

    private class EventHandler extends Handler {
        private static final int MSG_RECORD_STATUS_UPDATE = 1;
        private static final int MSG_MIC_STATUS_UPDATE = 2;
        private static final int MSG_RECORDING_COUNTDOWN = 3;

        EventHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            if (msg.what == MSG_RECORD_STATUS_UPDATE) {
                changeState((RecorderState) msg.obj);
            } else if (msg.what == MSG_MIC_STATUS_UPDATE) {
                updateMicStatus();
            } else if (msg.what == MSG_RECORDING_COUNTDOWN) {
                if (mState != RecorderState.STATE_RECORDING) {
                    return;
                }
                int cost = (int) msg.obj;
                millisUntilFinished = maxDurationMs - cost;
                millisUntilFinished = millisUntilFinished > 0 ? millisUntilFinished : 0;
                if (mListener != null) {
                    mListener.onTick(millisUntilFinished);
                }
            }
        }
    }
}
